#include "pipe.h"
#include "../event/event_manager.h"
#include "burnpipe.h"
#include "element/element_manager.h"
#include "threadpool/tp.h"
#include "utils/macro.h"
#include "utils/random.h"

namespace burn {

GPipeline::GPipeline() {
  session_ = URandom<>::generateSession(CGRAPH_STR_PIPELINE);
  thread_pool_ = UAllocator::safeMallocTemplateCObject<UThreadPool>(false);
  element_manager_ = CGRAPH_SAFE_MALLOC_COBJECT(GElementManager)
      param_manager_ = CGRAPH_SAFE_MALLOC_COBJECT(GParamManager)
      //   daemon_manager_ = CGRAPH_SAFE_MALLOC_COBJECT(GDaemonManager)
      event_manager_ = CGRAPH_SAFE_MALLOC_COBJECT(GEventManager)
}

GPipeline::~GPipeline() {
  //   CGRAPH_DELETE_PTR(daemon_manager_)
  CGRAPH_DELETE_PTR(element_manager_)
  CGRAPH_DELETE_PTR(param_manager_)
  CGRAPH_DELETE_PTR(event_manager_)
  CGRAPH_DELETE_PTR(thread_pool_)

  // 结束的时候，清空所有创建的节点信息。所有节点信息，仅在这一处释放
  for (GElementPtr element : element_repository_) {
    CGRAPH_DELETE_PTR(element)
  }
}

CStatus GPipeline::init() {
  CGRAPH_FUNCTION_BEGIN
  CGRAPH_ASSERT_INIT(false) // 必须是非初始化的状态下，才可以初始化。反之同理
  CGRAPH_ASSERT_NOT_NULL(thread_pool_)
  CGRAPH_ASSERT_NOT_NULL(element_manager_)
  CGRAPH_ASSERT_NOT_NULL(param_manager_)
  //   CGRAPH_ASSERT_NOT_NULL(daemon_manager_)
  CGRAPH_ASSERT_NOT_NULL(event_manager_)

  status += initSchedule();
  CGRAPH_FUNCTION_CHECK_STATUS

  status += param_manager_->init();
  status += event_manager_->init();
  status += element_manager_->init();
  //   status +=
  //       daemon_manager_->init(); //
  //       daemon的初始化，需要晚于所有element的初始化
  CGRAPH_FUNCTION_CHECK_STATUS

  is_init_ = true;
  CGRAPH_FUNCTION_END
}

CStatus GPipeline::run() {
  CGRAPH_FUNCTION_BEGIN
  CGRAPH_ASSERT_INIT(true)

  CGRAPH_ASSERT_NOT_NULL(element_manager_)
  CGRAPH_ASSERT_NOT_NULL(param_manager_)

  /**
   * 1. 将所有的 GParam 设置为初始值
   * 2. 执行dag逻辑
   * 3. 将所有的 GParam 复原
   */
   BPTIMER_START(param_setup);
  status = param_manager_->setup();
  CGRAPH_FUNCTION_CHECK_STATUS
  BPTIMER_STOP(param_setup);

  BPTIMER_START(elem_run);
  status += element_manager_->run();
  param_manager_->resetWithStatus(status);
  BPTIMER_STOP(elem_run);
  BLOG("[internal] param: %f, elem_run: %f", BPTIMER_MSEC(param_setup), BPTIMER_MSEC(elem_run));
  CGRAPH_FUNCTION_END
}

CStatus GPipeline::destroy() {
  CGRAPH_FUNCTION_BEGIN

  CGRAPH_ASSERT_INIT(true)
  CGRAPH_ASSERT_NOT_NULL(element_manager_)
  CGRAPH_ASSERT_NOT_NULL(param_manager_)
  //   CGRAPH_ASSERT_NOT_NULL(daemon_manager_)
  CGRAPH_ASSERT_NOT_NULL(event_manager_)

  status += event_manager_->destroy();
  //   status += daemon_manager_->destroy();
  status += element_manager_->destroy();
  status += param_manager_->destroy();
  CGRAPH_FUNCTION_CHECK_STATUS

  // 结束单个线程池信息
  status += thread_pool_->destroy();
  CGRAPH_FUNCTION_CHECK_STATUS

  is_init_ = false;
  CGRAPH_FUNCTION_END
}

CStatus GPipeline::process(CSize runTimes) {
  CGRAPH_FUNCTION_BEGIN
  status = init();
  CGRAPH_FUNCTION_CHECK_STATUS

  while (runTimes-- > 0) {
    status = run();
    CGRAPH_FUNCTION_CHECK_STATUS
  }

  status = destroy();
  CGRAPH_FUNCTION_END
}

CStatus GPipeline::dump(std::ostream &oss) {
  CGRAPH_FUNCTION_BEGIN
  CGRAPH_ASSERT_NOT_NULL(element_manager_)
  oss << "digraph CGraph {\n";
  oss << "compound=true;\n";

  for (const auto &element : element_manager_->manager_elements_) {
    CGRAPH_ASSERT_NOT_NULL(element)
    element->dump(oss);
  }

  oss << "}\n";
  CGRAPH_FUNCTION_END
}

GPipelinePtr GPipeline::setGElementRunTtl(CMSec ttl) {
  CGRAPH_ASSERT_INIT_RETURN_NULL(false)
  CGRAPH_ASSERT_NOT_NULL_RETURN_NULL(element_manager_)
  CGRAPH_ASSERT_NOT_NULL_RETURN_NULL(element_manager_->engine_)

  // 在element_manager中区执行信息了，所以ttl放到
  element_manager_->engine_->element_run_ttl_ = ttl;
  return this;
}

GPipelinePtr GPipeline::setGEngineType(GEngineType type) {
  CGRAPH_ASSERT_INIT_RETURN_NULL(false)
  CGRAPH_ASSERT_NOT_NULL_RETURN_NULL(element_manager_)

  element_manager_->setEngineType(type);
  return this;
}

GPipelinePtr
GPipeline::setUniqueThreadPoolConfig(const UThreadPoolConfig &config) {
  CGRAPH_FUNCTION_BEGIN
  CGRAPH_ASSERT_INIT_RETURN_NULL(false)
  CGRAPH_ASSERT_NOT_NULL_RETURN_NULL(thread_pool_)
  status = thread_pool_->setConfig(config);
  CGRAPH_CHECK_STATUS_RETURN_THIS_OR_NULL
}

CStatus GPipeline::initSchedule() {
  CGRAPH_FUNCTION_BEGIN
  CGRAPH_ASSERT_NOT_NULL(thread_pool_)
  CGRAPH_ASSERT_NOT_NULL(event_manager_)
  CGRAPH_ASSERT_NOT_NULL(element_manager_)

  status += thread_pool_->init();
  CGRAPH_FUNCTION_CHECK_STATUS

  event_manager_->setThreadPool(thread_pool_);
  element_manager_->setThreadPool(thread_pool_);

  // 设置所有的element 中的thread_pool
  for (auto &iter : this->element_repository_) {
    CGRAPH_ASSERT_NOT_NULL(iter)
    iter->setThreadPool(thread_pool_);
  }

  CGRAPH_FUNCTION_END
}

CStatus GPipeline::AddElementManager(GElementPtr eptr) {
  return element_manager_->add(eptr);
}

} // namespace burn